package com.yehebl.handler;

import com.alibaba.fastjson.JSONObject;
import com.esotericsoftware.reflectasm.MethodAccess;
import com.yehebl.handler.annotaction.Body;
import com.yehebl.handler.convert.DataConvert;
import com.yehebl.handler.convert.IDataConvert;
import com.yehebl.handler.exception.ParamsNotNullException;
import com.yehebl.handler.manager.*;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;
import java.util.Map;
import java.util.Set;

public class HandlerUtil {

    private InitMain initMain;

    private ParameterTypeManager parameterTypeManager;

    private MethodManager methodManager;

    private ReturnTypeManager returnTypeManager;

    private MethodParamNamesManager methodParamNamesManager;


    private InstanceManager instanceManager;

    private NotNullManager notNullManager;

    public HandlerUtil() {
        initMain = new InitMain();
        parameterTypeManager = new ParameterTypeManager();
        methodManager = new MethodManager();
        returnTypeManager = new ReturnTypeManager();
        methodParamNamesManager = new MethodParamNamesManager();
        instanceManager=new InstanceManager();
        notNullManager=new NotNullManager();
    }

    public void setIGetInstance(IGetInstance getInstance) {
        initMain.setGetInstance(getInstance);
    }

    public void init(String pack) {
        initMain.loadConfig(this, pack);
    }

    public boolean isExistType(String key, Class<?> clazz) {
        return parameterTypeManager.isExistType(key, clazz);
    }

    public Method getMethod(String key) {
        Method method = methodManager.get(key);
        return method;
    }

    public void setData(String key, Map<Object, Object> map, String jsonStr) {
        if (jsonStr == null || jsonStr.trim().equals("")) {
            return;
        }
        JSONObject json = new JSONObject();

        Class<?>[] classes = parameterTypeManager.get(key);
        Method method = getMethod(key);
        Type[] types = method.getGenericParameterTypes();
        String[] params = methodParamNamesManager.get(key);
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        for (int i = 0; i < classes.length; i++) {
            Annotation[] annotations = parameterAnnotations[i];
            boolean flag = false;
            if (annotations != null) {
                for (int j = 0; j < annotations.length; j++) {
                    if (annotations[j].annotationType() == Body.class) {
                        json.put(params[i], jsonStr);
                        IDataConvert convert = null;
                        if (classes[i].isAssignableFrom(List.class)) {
                            convert = DataConvert.get(List.class);
                            ParameterizedType pType = (ParameterizedType) types[i];
                            convert.convert(map, json, params[i], pType.getActualTypeArguments()[0].getClass());
                            continue;
                        } else if (classes[i].isAssignableFrom(Map.class)) {
                            ParameterizedType pType = (ParameterizedType) types[i];
                            convert = DataConvert.get(Map.class);
                            convert.convert(map, json, params[i], pType.getActualTypeArguments()[0].getClass());
                            continue;
                        }
                        convert = DataConvert.get(classes[i]);
                        if (convert == null) {
                            convert = DataConvert.get(classes[i].getName());
                        }
                        if (convert == null) {
                            convert = DataConvert.get("other");
                        }
                        convert.convert(map, json, params[i], classes[i]);
                        flag = true;
                        break;
                    }
                }
            }
            if (flag) {
                continue;
            }
            if (map.containsKey(classes[i]) || map.containsKey(params[i])) {
                continue;
            }
            try {
                json = JSONObject.parseObject(jsonStr);
            } catch (Exception e) {
                json = new JSONObject();
            }
            IDataConvert convert = null;
            if (classes[i].isAssignableFrom(List.class)) {
                convert = DataConvert.get(List.class);
                ParameterizedType pType = (ParameterizedType) types[i];
                convert.convert(map, json, params[i], (Class<?>) pType.getActualTypeArguments()[0]);
                continue;
            } else if (classes[i].isAssignableFrom(Map.class)) {
                ParameterizedType pType = (ParameterizedType) types[i];
                convert = DataConvert.get(Map.class);
                convert.convert(map, json, params[i], (Class<?>) pType.getActualTypeArguments()[0]);
                continue;
            }
            if (convert == null) {
                convert = DataConvert.get(classes[i]);
            }
            if (convert == null) {
                convert = DataConvert.get(classes[i].getName());
            }
            if (convert == null) {
                convert = DataConvert.get("other");
            }
            convert.convert(map, json, params[i], classes[i]);
        }
    }

    public Object handler(String key, Map<Object, Object> map) throws Exception {
        Object object = instanceManager.get(key);
        MethodAccess methodAccess = MethodAccessManager.get(object.getClass());
        Method method = methodManager.get(key);
        String methodName = method.getName();
        Class<?>[] classes = parameterTypeManager.get(key);
        boolean isResult = !returnTypeManager.get(key).equals("void");
        if (classes == null) {
            if (isResult) {
                return methodAccess.invoke(object, methodName);
            }
            methodAccess.invoke(object, methodName);
            return null;
        }
        Object[] objs = new Object[classes.length];
        String[] paramNames = methodParamNamesManager.get(key);
        Set<String> set = notNullManager.get(key);
        for (int i = 0; i < classes.length; i++) {
            Object object2 = null;
            if (map.containsKey(paramNames[i])) {
                //参数名寻找
                object2 = map.get(paramNames[i]);
            }
            if (object2 == null && map.containsKey(classes[i])) {
                //参数类型查找
                object2 = map.get(classes[i]);
            }
            if (object2 == null) {
                for (Object obj : map.keySet()) {
                    if (obj instanceof Class<?>) {
                        //父类传递,子类接受
                        if (((Class<?>) obj).isAssignableFrom(classes[i])) {
                            object2 = map.get(obj);
                            break;
                        }
                    }
                }
            }
            objs[i] = object2;
            if (set != null && objs[i] == null && set.contains(paramNames[i])) {
                throw new ParamsNotNullException(paramNames[i]);
            }
        }
        if (isResult) {
            return methodAccess.invoke(object, methodName, classes, objs);
        }
        methodAccess.invoke(object, methodName, objs);
        return null;
    }

    public InitMain getInitMain() {
        return initMain;
    }

    public void setInitMain(InitMain initMain) {
        this.initMain = initMain;
    }

    public ParameterTypeManager getParameterTypeManager() {
        return parameterTypeManager;
    }

    public void setParameterTypeManager(ParameterTypeManager parameterTypeManager) {
        this.parameterTypeManager = parameterTypeManager;
    }

    public MethodManager getMethodManager() {
        return methodManager;
    }

    public void setMethodManager(MethodManager methodManager) {
        this.methodManager = methodManager;
    }

    public ReturnTypeManager getReturnTypeManager() {
        return returnTypeManager;
    }

    public void setReturnTypeManager(ReturnTypeManager returnTypeManager) {
        this.returnTypeManager = returnTypeManager;
    }

    public MethodParamNamesManager getMethodParamNamesManager() {
        return methodParamNamesManager;
    }

    public void setMethodParamNamesManager(MethodParamNamesManager methodParamNamesManager) {
        this.methodParamNamesManager = methodParamNamesManager;
    }

    public InstanceManager getInstanceManager() {
        return instanceManager;
    }

    public void setInstanceManager(InstanceManager instanceManager) {
        this.instanceManager = instanceManager;
    }

    public NotNullManager getNotNullManager() {
        return notNullManager;
    }

    public void setNotNullManager(NotNullManager notNullManager) {
        this.notNullManager = notNullManager;
    }
}
